/*
 * Copyright (C), 2014-2016, org.esaydev
 * FileName: WechatLoginFilter.java
 * Author:   jian
 * Date:     2016年3月22日 
 */
package org.easydev.wechat.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.easydev.wechat.common.constants.WechatConfig;
import org.easydev.wechat.common.util.CookieUtil;
import org.easydev.wechat.common.util.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

/**
 * <Description> 微信登陆filter<br>
 */
public class WechatLoginFilter implements Filter {

    /**
     * static LOGGER
     */
    private static Logger LOGGER = LoggerFactory.getLogger(WechatLoginFilter.class);

    /**
     * 
     */
    private static final String KEY_NAME_CODE = "CODE";

    /**
     * 
     */
    private static final String KEY_NAME_APPID = "APPID";

    /**
     * 
     */
    private static final String KEY_NAME_STATE = "STATE";

    /**
     * 
     */
    private static final String KEY_NAME_APPSECRET = "APPSECRET";

    /**
     * 
     */
    private static final String KEY_NAME_REDIRECT_URI = "REDIRECT_URI";

    /**
     * 
     */
    private static final String KEY_NAME_BASIC_TYPE = "BASIC_TYPE";

    /**
     * 
     */
    private static final String GETACCESS_TOKENURL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code";

    /**
     * 
     */
    private static final String REDIRECTURL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=BASIC_TYPE&state=STATE#wechat_redirect";

    /**
     * 微信登陆处理，将openId放入session和cookie中 <br>
     * <br>
     * {@inheritDoc}
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        LOGGER.debug("[wechat login] enter filter... ");
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        if (null == httpRequest || null == httpResponse) {
            return;
        }
        String openId = CookieUtil.getCookieValue("easyOpenId", httpRequest);
        if (StringUtils.isNotBlank(openId) || !isFromWxBrower(httpRequest)) {
            LOGGER.debug("[wechat login] filter params... url:{}, method:{},openId:{}", httpRequest.getRequestURL(), httpRequest.getMethod(), openId);
            chain.doFilter(request, response);
            return;
        }
        LOGGER.debug("[wechat login] filter params... url:{}, method:{},openId is null", httpRequest.getRequestURL(), httpRequest.getMethod());
        boolean isLogon = false;
        String state = request.getParameter("state");
        if ("EASY_LOGIN".equals(state)) {
            isLogon = dealWxLoginRedirect(httpRequest, httpResponse);
        } else {
            isLogon = redirectToWxForLogin(httpRequest, httpResponse);
        }
        if (isLogon) {
            chain.doFilter(request, response);
        }
    }

    /**
     * Description: 处理重定向微信登陆status的回调<br>
     * 
     * @param request <br>
     * @param response <br>
     * @return <br>
     * @throws IOException
     * @throws UnsupportedEncodingException
     * @throws Exception <br>
     */
    private boolean dealWxLoginRedirect(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, IOException {
        String code = request.getParameter("code");
        LOGGER.info("[login process] dealWxLoginRedirect begin! code:{}", code);
        if (StringUtils.isNotBlank(code)) {
            String getAccess_tokenUrl = GETACCESS_TOKENURL.replace(KEY_NAME_CODE, code).replace(KEY_NAME_APPID, WechatConfig.getAppId())
                    .replace(KEY_NAME_APPSECRET, WechatConfig.getAppSecret());
            String message = HttpUtil.sendGet(getAccess_tokenUrl);
            JSONObject json = JSONObject.parseObject(message);
            if (null != json.get("openid")) {
                CookieUtil.addCookie("easyOpenId", json.getString("openid"), response);
            }
            LOGGER.info("[login process] dealWxLoginRedirect success! openid:{}", json.get("openid"));
            return true;
        }
        // 若用户禁止授权，则重定向后不会带上code参数，仅会带上state参数 不需要再重新跳了
        // return redirectToWxForLogin(request, response);
        return false;
    }

    /**
     * Description: 重定向到微信state=EASY_LOGIN<br>
     * 
     * @param request <br>
     * @param response <br>
     * @return <br>
     * @throws UnsupportedEncodingException <br>
     * @throws IOException <br>
     */
    private boolean redirectToWxForLogin(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, IOException {
        String qureyStr = request.getQueryString();
        String url = StringUtils.isBlank(qureyStr) ? request.getRequestURL().toString() : request.getRequestURL().append("?" + qureyStr).toString();
        url = URLEncoder.encode(url, "utf-8");
        // 此处采用静默授权的方式，因为只为取openid，若要授权页面，只需改成snsapi_userinfo
        String redirectUrl = REDIRECTURL.replace(KEY_NAME_APPID, WechatConfig.getAppId()).replace(KEY_NAME_REDIRECT_URI, url)
                .replace(KEY_NAME_BASIC_TYPE, "snsapi_base").replace(KEY_NAME_STATE, "EASY_LOGIN");
        response.sendRedirect(redirectUrl);
        LOGGER.info("[login process] redirectToWxForLogin url:{} ", redirectUrl);
        return false;
    }

    /**
     * 判断是否微信浏览器访问
     * 
     * @param request 用户请求
     * @return <br>
     */
    private static boolean isFromWxBrower(HttpServletRequest request) {
        String theUserAgentHeader = request.getHeader("USER-AGENT").toLowerCase();
        return StringUtils.isNotBlank(theUserAgentHeader) && theUserAgentHeader.contains("micromessenger");
    }

    /**
     * {@inheritDoc}
     */
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * {@inheritDoc}
     */
    public void destroy() {

    }

}
